const path = require('path');
const fs = require('fs');
const compose = require('koa-compose');
import Request from '../libs/Request';
import Response from '../libs/Response';
import Helper from '../libs/Helper';
import Curl from '../libs/Curl';
import serviceProxy from '../libs/Model';

const db = uniCloud.database();

export default class Router {
	// 没想好怎么处理，暂时这里
	#app_key = Symbol(new Date().toDateString());
	constructor(event, context, config = {}) {
		const singlePaths = {};
		/**
		 * 递归加载controller or service
		 */
		const lazyProxy = function (appInstance, parentPath = '') {
			return new Proxy(
				function () {
					this.path = this.path ? path.join(this.path, parentPath) : parentPath;
				},
				{
					//取属性递归代理
					get: function (target, key) {
						let instance = new target();
						if (instance.path) {
							return lazyProxy(appInstance, path.join(instance.path, key), path);
						}
						return target[key];
					},
					//调用函数代理
					apply: function (target, ctx, args) {
						let instance = new target();
						// 是否 service
						let isService = instance.path.startsWith('service');
						// 判断是否统一service 配置 app_unify_service
						if (isService && appInstance.$dx.getConfig('app_unify_service') === true) {
							instance.path = path.join(appInstance.app_path, instance.path);
						} else {
							instance.path = path.join(appInstance.app_module_path, instance.path);
						}
						let singleAppPath = path.dirname(instance.path) + '.js',
							action = path.basename(instance.path);
						if (singlePaths[singleAppPath] === void 0) {
							let isExists = appInstance.getStat(singleAppPath);
							if (isExists && isExists.isFile()) {
								singlePaths[singleAppPath] = singleAppPath;
							} else {
								throw new Error(instance.path + ' is not found');
							}
						}
						//
						const appClass = require(singleAppPath);
						let model = new appClass(appInstance.$dx);
						// controller or service
						if (isService) {
							model = serviceProxy(model);
						} else {
							//跨控制器调用也需要增加语言加载，待开发
						}
						if (typeof model[action] != 'function') {
							throw new Error(action + ' method is not found');
						}
						//调用函数
						return model[action](...args);
					},
				}
			);
		};
		// 应用路径
		this.app_path = '';
		this.app_module_path = '';
		let privateVariable = ['_config', '_lang'];
		// 订阅的数据
		let _subscribe = {};
		// 当前实例
		let app = this;
		let redis = null;
		if (config.app_cache === 'redis') {
			redis = uniCloud.redis();
		}
		//实例
		this.$dx = new Proxy(
			{
				// 应用配置
				_config: config,
				//应用语言
				_lang: {
					'successfully added': '添加成功',
					'failed to add': '添加失败',
					'parameter error': '%s参数错误',
					'incorrect data format inserted': '新增的数据,格式错误',
					'record not found': '记录未找到',
					'edit succeeded': '编辑成功',
					'edit failed': '编辑失败',
					'delete failed': '删除失败',
					'deletion succeeded': '删除成功',
					'operation succeeded': '操作成功',
					'operation failed': '操作失败',
					'no permission to operate': '没有权限操作',
				},
				// db
				db,
				// 助手函数
				utils: Helper,
				// curl
				Curl,
				// 请求
				request: new Request(event, context, config),
				// 响应
				response: new Response(event, context),
				// 懒加载控制器
				controller: lazyProxy(app, 'controller'),
				// 懒加载服务
				service: lazyProxy(app, 'service'),
				redis,
				//订阅
				$on(name, callback) {
					if (typeof callback != 'function') {
						throw new Error('缺少回调函数');
					}
					if (_subscribe[name] === void 0) {
						_subscribe[name] = [];
					}
					_subscribe[name].push(callback);
				},
				// 一次订阅
				$once(name, callback) {
					if (typeof callback != 'function') {
						throw new Error('缺少回调函数');
					}
					if (_subscribe[name] === void 0) {
						_subscribe[name] = [];
					}
					let that = this;
					function on(...args) {
						// 自动移除
						that.$off(name, on);
						callback.apply(app, args);
					}
					//同时保证手动移除
					on.callback = callback;
					_subscribe[name].push(on);
				},
				// 触发
				$emit(name, ...args) {
					if (_subscribe[name] === void 0) {
						return;
					}
					for (let cb of _subscribe[name]) {
						cb.apply(app, args);
					}
				},
				// 取消订阅
				$off(name, callback) {
					if (_subscribe[name] === void 0) {
						return;
					}
					if (!callback) {
						_subscribe[name].length = 0;
					}
					//找到移除
					for (let i = 0, len = _subscribe[name].length; i < len; i++) {
						if (_subscribe[name][i] === callback || _subscribe[name][i].callback === callback) {
							_subscribe[name].splice(i, 1);
							break;
						}
					}
				},
				//获取语言
				$t: function (name, ...args) {
					let langStr = this.getLang(name);
					//取当前控制器的语言，或公共的语言
					if (langStr) {
						return langStr.format(...args);
					} else {
						return name;
					}
				},
				//成功
				success(message = '操作成功', data = {}, code = 1000) {
					return {
						message,
						data,
						code,
					};
				},
				// 失败
				fail(message = '操作失败', data = {}, code = 4000) {
					return {
						message,
						data,
						code,
					};
				},
				// 抛出自定义信息
				error(message, data = {}, code = 4000) {
					throw {
						message,
						data,
						code,
					};
				},
				async getUserInfo(uid) {
					let res = await db.collection('uni-id-users').doc(uid).get();
					return res.data.length ? res.data[0] : null;
				},
				async updateUserInfo(uid, data) {
					return await db.collection('uni-id-users').doc(uid).update(data);
				},
			},
			{
				get(target, name) {
					// _下划线开头的
					if (privateVariable.includes(name)) {
						throw new Error(name + ' is a private variable');
					}
					// 获取配置
					if (name === 'getConfig') {
						return function (key) {
							return key ? target._config[key] : { ...target._config };
						};
					}
					//  获取语言
					if (name === 'getLang') {
						return function (key) {
							return key ? target._lang[key] : '';
						};
					}
					//  设置语言
					if (name === 'setLang') {
						return function (key, data) {
							if (app.#app_key === key) {
								target._lang = Object.assign({}, target._lang, data);
							} else {
								throw new Error('setLang is a private function');
							}
						};
					}
					//  设置语言
					if (name === 'setUtils') {
						return function (key, data) {
							if (app.#app_key === key) {
								target.utils = Object.assign({}, target.utils, data);
							} else {
								throw new Error('setUtils is a private function');
							}
						};
					}
					return Reflect.get(target, name);
				},
				set(target, key, value) {
					if (['uniID', 'auth'].includes(key)) {
						return Reflect.set(target, key, value);
					}
					return false;
				},
			}
		);
	}
	//判断文件
	getStat(path) {
		try {
			return fs.statSync(path);
		} catch (e) {}
		return false;
	}
	//添加中间件
	async addMiddleware() {
		let { $dx } = this;
		//中间件判断追加
		const initMiddleware = function (fn, condition, action) {
			const { enable, ignore } = condition;
			//启用
			if (enable) {
				//是否排除的
				if ($dx.utils.isEmpty(ignore)) {
					return fn;
				} else if (typeof ignore === 'string' && !new RegExp('^' + ignore, 'g').test(action)) {
					return fn;
				} else if ($dx.utils.isArray(ignore) && !ignore.some((item) => new RegExp('^' + item, 'g').test(action))) {
					return fn;
				}
			}
			return false;
		};
		const app_middleware = $dx.getConfig('app_middleware');
		//中间件判断
		if (app_middleware !== void 0 && $dx.utils.isArray(app_middleware)) {
			let middle_array = [];
			//分发前执行中间件
			app_middleware.map((item) => {
				//判断是否注册中间件item[0] item[1]
				let fn = initMiddleware(item[0], item[1], $dx.request.url);
				if (typeof fn === 'function') {
					middle_array.push(fn);
				}
			});
			await compose(middle_array)(this);
		}
	}
	// 应用运行
	async run() {
		try {
			let app_dir = this.$dx.getConfig('app_dir');
			let app_name = this.$dx.getConfig('app_name');
			let app_helper = this.$dx.getConfig('app_helper');
			let isExists;
			const app_multi_module = this.$dx.getConfig('app_multi_module');
			//是否配置运行目录
			if (!app_dir) {
				throw new Error('未配置 app_dir');
			}
			// 应用路径
			let appPath = path.join(app_dir, app_name);
			//判断应用目录
			isExists = this.getStat(appPath);
			if (!isExists || !isExists.isDirectory()) {
				throw new Error(app_name + ' is not found');
			}
			this.app_path = appPath;
			// 判断是否是定时调用
			if (this.$dx.request.isTriggerTimer) {
				let app_trigger = this.$dx.getConfig('app_trigger');
				if (app_trigger) {
					switch (typeof app_trigger) {
						case 'function':
							await app_trigger.call(this);
							break;
						case 'string':
							const triggerPath = path.join(app_dir, app_trigger);
							// 判断文件
							isExists = this.getStat(triggerPath);
							if (isExists && isExists.isFile()) {
								//读取
								const triggerFile = require(triggerPath);
								if (typeof triggerFile === 'function') {
									await triggerFile.call(this);
								}
							}
							break;
					}
				}
				return 'end';
			}

			//开始挂载钩子
			let app_hooks = this.$dx.getConfig('app_hooks');
			if (app_hooks != void 0) {
				for (let [key, value] of Object.entries(app_hooks)) {
					// 配置是函数直接订阅
					if (typeof value === 'function') {
						this.$dx.$on(key, value);
					} else if (typeof value === 'string') {
						// 配置是文件
						const hookPath = path.join(app_dir, value);
						// 判断文件
						isExists = this.getStat(hookPath);
						if (isExists && isExists.isFile()) {
							//读取
							const hookFile = require(hookPath);
							if (typeof hookFile === 'function') {
								// 订阅
								this.$dx.$on(key, hookFile);
							}
						}
					}
				}
			}
			/** 挂在钩子完毕 **/

			// 中间件校验
			await this.addMiddleware();
			// 中间件校验结束

			// 挂在自定义助手函数
			let appHelperPath = path.join(app_dir, app_helper + '.js');
			isExists = this.getStat(appHelperPath);
			if (isExists && isExists.isFile()) {
				this.$dx.setUtils(this.#app_key, require(appHelperPath));
			}
			//应用初始化
			this.$dx.$emit('app_init');

			//加载公共语言
			let lang = this.$dx.request.lang;
			let langPath = path.join(path.join(app_dir, 'lang'), lang + '.js');
			isExists = this.getStat(langPath);
			if (isExists && isExists.isFile()) {
				this.$dx.setLang(this.#app_key, require(langPath));
			}
			//公共言加载结束

			//判断是否是多模块
			let appModulePath = appPath;
			if (app_multi_module) {
				const module = this.$dx.request.module;
				console.log('module', module);
				appModulePath = path.join(appPath, module);
				//判断模块目录
				isExists = this.getStat(appModulePath);
				if (!isExists || !isExists.isDirectory()) {
					throw new Error('module ' + module + ' is not found');
				}
			}
			this.app_module_path = appModulePath;
			//模块目录语言
			let controllerLangPath = appModulePath;
			//控制器文件
			let controllerPath = path.join(appModulePath, 'controller');
			//多级控制器【index.index.js】
			let controllerName = this.$dx.request.controller.replace(/\./g, '/');
			//控制器js文件是否存在
			isExists = this.getStat(path.join(controllerPath, controllerName + '.js'));
			if (!isExists || !isExists.isFile()) {
				throw new Error('controller ' + controllerName + ' is not found');
			}
			//加载控制器
			const controller = require(path.join(controllerPath, controllerName + '.js'));
			//挂载控制器对应的语言文件
			controllerLangPath = path.join(path.join(path.join(controllerLangPath, 'lang'), lang), controllerName + '.js');
			isExists = this.getStat(controllerLangPath);
			if (isExists && isExists.isFile()) {
				this.$dx.setLang(this.#app_key, require(controllerLangPath));
			}
			//实例控制器方法
			const controllerInstance = new controller(this.$dx);
			//方法是否存在
			const action = this.$dx.request.action;
			if (typeof controllerInstance[action] != 'function') {
				throw new Error(`action ${action} is not found`);
			}
			//调用控制器开始
			this.$dx.$emit('action_begin');
			//执行方法
			let result = await controllerInstance[action](this.$dx.request.param(), this.$dx.request.header());
			this.$dx.response.setBody(result);
			const body = this.$dx.response.body;
			//调用初始化
			this.$dx.$emit('app_end', body);
			// 返回数据
			return body;
		} catch (e) {
			//返回的信息
			if (e.code && e.message) {
				return e;
			}
			//开启调试，抛出错误
			if (this.$dx.getConfig('app_debug')) {
				throw e;
			}
		}
		return '未返回数据';
	}
}
